package com.uam.core.service;

import java.awt.geom.Point2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

import com.uam.core.entity.PointInfoData;
import com.uam.core.entity.uitls.GeoUtil;
import com.uam.core.lk.utils.fencebuffer.BufferBase;
import com.uam.core.lk.utils.fencebuffer.PolygonBufferImpl;
import com.uam.core.scheduler.PointInfoScheduler;
import com.uam.core.tool.GetOldPointTool;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.geotools.geojson.GeoJSONUtil;
import org.geotools.geojson.geom.GeometryJSON;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.operation.buffer.BufferOp;
import org.locationtech.jts.operation.buffer.BufferParameters;
import org.locationtech.jts.util.GeometricShapeFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.elasticsearch.core.convert.GeoConverters.GeoJsonToMapConverter;
import org.springframework.data.elasticsearch.core.geo.GeoJsonLineString;
import org.springframework.data.elasticsearch.core.geo.GeoJsonPoint;
import org.springframework.data.geo.Point;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mapbox.turf.TurfConstants;
import com.mapbox.turf.TurfMeasurement;
import com.uam.core.dao.elasticsearch.ElasticsearchDao;
import com.uam.core.dao.mysql.AircraftDao;
import com.uam.core.dao.mysql.SectorStatDao;
import com.uam.core.dao.mysql.StationDao;
import com.uam.core.dao.mysql.TaskDao;
import com.uam.core.dao.mysql.TaskStatDao;
import com.uam.core.entity.constant.Constant;
import com.uam.core.entity.enums.BaseStationTypes;
import com.uam.core.entity.enums.ElasticsearchIndices;
import com.uam.core.entity.enums.FlyModes;
import com.uam.core.entity.enums.RouteParams;
import com.uam.core.entity.enums.SignalStrengths;
import com.uam.core.entity.enums.SpatialLevels;
import com.uam.core.entity.model.AircraftModel;
import com.uam.core.entity.model.ElasticsearchQueryResult;
import com.uam.core.entity.model.ElasticsearchQueryResult.AreaFields;
import com.uam.core.entity.model.ElasticsearchQueryResult.BaseStationFields;
import com.uam.core.entity.model.ElasticsearchQueryResult.CommonFields;
import com.uam.core.entity.model.ElasticsearchQueryResult.RouteComponentFields;
import com.uam.core.entity.model.ElasticsearchQueryResult.RouteIntegrationFields;
import com.uam.core.entity.model.ObstacleArea;
import com.uam.core.entity.model.PreviewTrack;
import com.uam.core.entity.model.SectorStatModel;
import com.uam.core.entity.model.Signal;
import com.uam.core.entity.model.StationQueryResult;
import com.uam.core.entity.model.TaskModel;
import com.uam.core.entity.model.TaskModel.Status;
import com.uam.core.entity.model.TaskQueryResult;
import com.uam.core.entity.model.TaskStatModel;
import com.uam.core.entity.uitls.HttpUtil;
import com.uam.core.entity.uitls.SpatialUtil;
import com.uam.core.entity.uitls.StringUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 航线管理
 *
 * @author lishf
 */
@SuppressWarnings("deprecation")
@Service
@Slf4j
public class RouteService {

	public static final int[] OPERATION_FAILURE_STATUS = new int[0];

	private static final Map<SignalStrengths, Map<BaseStationTypes, Integer>> SIGNAL_CONFIG = new HashMap<SignalStrengths, Map<BaseStationTypes, Integer>>();

	public static final Map<BaseStationTypes, Integer> NONE_SIGNAL_CONFIG = new HashMap<BaseStationTypes, Integer>();

	static {
		Map<BaseStationTypes, Integer> signalTypeConfig = new HashMap<BaseStationTypes, Integer>();
		signalTypeConfig.put(BaseStationTypes.COMMUNICATION, Integer.valueOf(2));
		signalTypeConfig.put(BaseStationTypes.NAVIGATION, Integer.valueOf(2));
		signalTypeConfig.put(BaseStationTypes.SUPERVISORY, Integer.valueOf(10));
		RouteService.SIGNAL_CONFIG.put(SignalStrengths.STRONG, signalTypeConfig);
		signalTypeConfig = new HashMap<BaseStationTypes, Integer>();
		signalTypeConfig.put(BaseStationTypes.COMMUNICATION, Integer.valueOf(10));
		signalTypeConfig.put(BaseStationTypes.NAVIGATION, Integer.valueOf(10));
		signalTypeConfig.put(BaseStationTypes.SUPERVISORY, Integer.valueOf(50));
		RouteService.SIGNAL_CONFIG.put(SignalStrengths.MIDDLE, signalTypeConfig);
		signalTypeConfig = new HashMap<BaseStationTypes, Integer>();
		signalTypeConfig.put(BaseStationTypes.COMMUNICATION, Integer.valueOf(50));
		signalTypeConfig.put(BaseStationTypes.NAVIGATION, Integer.valueOf(50));
		signalTypeConfig.put(BaseStationTypes.SUPERVISORY, Integer.valueOf(75));
		RouteService.SIGNAL_CONFIG.put(SignalStrengths.WEAK, signalTypeConfig);
		RouteService.NONE_SIGNAL_CONFIG.put(BaseStationTypes.COMMUNICATION, Integer.valueOf(100));
		RouteService.NONE_SIGNAL_CONFIG.put(BaseStationTypes.NAVIGATION, Integer.valueOf(100));
		RouteService.NONE_SIGNAL_CONFIG.put(BaseStationTypes.SUPERVISORY, Integer.valueOf(150));
	}

	@Autowired
	private TaskDao taskDao = null;

	@Autowired
	private StationDao stationDao = null;

	@Autowired
	private ElasticsearchDao elasticsearchDao = null;

	@Value("${air.route.url}")
	private String airRouteUrl = null;

	@Value("${road.net.route.url}")
	private String roadNetRouteUrl = null;

	@Value("${route.altitude.need.base}")
	private boolean altitudeNeedBase = false;

	@Value("${system.time.buffer.seconds}")
	private int systemTimeBuffer = 0;

	@Value("${extract.angle}")
	private double extractAngle = 0.00D;

	@Value("${cross.case.need.hover}")
	private boolean crossCaseNeedHover = false;

	@Value("${station.buffer.meter}")
	private double stationBuffer = 0.00D;

	@Value("${startup.and.shutdown.mode}")
	private boolean startupAndShutdownMode = false;

	@Value("${compute.altitude.mode}")
	private boolean computeAltitudeMode = false;

	@Value("${aircraft.is.obstacle}")
	private boolean aircraftIsObstacle = false;

	private Random random = new Random();
	@Value("${circle.edge.num}")
	private int circleEdgeNum = 0;


	@Value("${earth.radius}")
	private double earthRadius = 0.00D;


	private GeometryFactory geometryFactory = new GeometryFactory();

	PolygonBufferImpl polygonBuffer=new PolygonBufferImpl();

	private volatile boolean enableNoFlyArea = false;



	@SuppressWarnings("unchecked")
	public void handleRejectedResult(int taskId, String remark, List<Map<String, Object>> aircraftPositions) {
		this.taskDao.rejectTask(taskId, remark);
		if (aircraftPositions == null) {
			return;
		}
		ElasticsearchQueryResult sectorResult = this.elasticsearchDao.query(QueryBuilders.matchAllQuery(), 1, 10000,
				new String[] { CommonFields.NAME.getName(), AreaFields.BORDER.getName() }, new String[0],
				ElasticsearchIndices.SECTOR_INDEX);
		if (ElasticsearchQueryResult.EMPTY != null) {
			int i = 0;
			int num = aircraftPositions.size();
			List<org.locationtech.jts.geom.Point> aircraftPoints = new ArrayList<org.locationtech.jts.geom.Point>(num);
			while (i < num) {
				Map<String, Object> aircraftPosition = (Map<String, Object>) aircraftPositions.get(i++)
						.get(CommonFields.POSITION.getName());
				aircraftPoints.add(this.geometryFactory.createPoint(
						new Coordinate(((Double) aircraftPosition.get(CommonFields.LON.getName())).doubleValue(),
								((Double) aircraftPosition.get(CommonFields.LAT.getName())).doubleValue())));

			}
			i = 0;
			int finalNum = Integer.MIN_VALUE;
			String finalSectorId = null;
			String finalSectorName = null;
			List<Map<String, Object>> sectors = sectorResult.getData();
			int size = sectors.size();
			while (i < size) {
				int j = 0;
				int currNum = 0;
				Map<String, Object> sector = sectors.get(i++);
				try {
					Geometry geometry = new GeometryJSON().read(GeoJSONUtil.toReader(
							JSON.toJSONString((Map<String, Object>) sector.get(AreaFields.BORDER.getName()))));
					while (j < num) {
						if (aircraftPoints.get(j++).intersects(geometry)) {
							++currNum;
						}
					}
				} catch (Throwable e) {
					RouteService.log.error(e.getMessage(), e);
				}
				if (currNum > finalNum) {
					finalNum = currNum;
					finalSectorId = (String) sector.get(CommonFields.ID.getName());
					finalSectorName = (String) sector.get(CommonFields.NAME.getName());
				}
			}
//			if (!StringUtil.isEmpty(finalSectorId)) {
//				finalSectorId = finalSectorId.trim();
//				if (finalSectorId.length() != 0) {
//					this.sectorStatDao.saveSectorCapacity(
//							new SectorStatModel(finalSectorId, finalSectorName, -1, null, finalNum));
//				}
//			}
		}
	}

	public JSONObject pickRoute(double altitude, double maxDuration, int size, JSONArray routes) {
		int i = 0;
		SpatialLevels[] spatialLevels = SpatialLevels.values();
		while (i < size) {
			JSONObject route = routes.getJSONObject(i++);
			int floor = route.getIntValue("floor");
			SpatialLevels spatialLevel = spatialLevels[floor <= 0 ? 0 : (floor - 1)];
			double minAltitude = spatialLevel.getMinAltitude();
			if ( route.getDoubleValue("distance") < maxDuration){
				route.put("min_altitude", Double.valueOf(minAltitude));
				route.put("max_altitude", Double.valueOf(spatialLevel.getMaxAltitude()));
				return route;
			}
		}
		return null;
	}


	public double[] position2Array(JSONArray position) {
		int i = 0;
		return new double[] { position.getDoubleValue(i++), position.getDoubleValue(i) };
	}



	private int computeCost(double distance, double acceleration, double speed, double elapsedCost) {
		return (int) (this.computeCost(acceleration, 0.00D, speed) - elapsedCost + (distance
				- (Math.pow(speed, 2.00D) - Math.pow(acceleration * elapsedCost, 2.00D)) / (acceleration * 2.00D))
				/ speed);
	}

	public double persistence(double minAltitude, double maxAltitude, TaskModel task, AircraftModel aircraft,
							   int coordinateNum, JSONArray track, double startAltitude, double endAltitude, List<PointInfoData> data1,boolean isUpdateDb) {
		int i = 0;
		int remaining = coordinateNum + 2;
		double extractAngleCosine = Math.cos(this.extractAngle * 0.0174533D);
		double validLastLon = Integer.MIN_VALUE;
		double validLastLat = Integer.MIN_VALUE;
		int taskIdValue = task.getId();
		String taskId = String.valueOf(taskIdValue);
		double speedValue = aircraft.getSpeed();
		double halfSpeedValue = speedValue / 2.00D;
		Double speed = Double.valueOf(speedValue);
		speedValue = speedValue / 3.60D;
		Double halfSpeed = Double.valueOf(halfSpeedValue);
		double altitudeValue = this.computeAltitudeMode
				? (this.random.nextInt((int) (maxAltitude - minAltitude)) + minAltitude)
				: ((maxAltitude + minAltitude) / 2.00D);
		double startVerticalDistanceValue = altitudeValue - startAltitude;
		double launchBuffer = this.stationBuffer + startVerticalDistanceValue;
		Double altitude = Double.valueOf(altitudeValue);
		Double startVerticalDistance = Double.valueOf(startVerticalDistanceValue);
		double startupAccelerationValue = Math.pow(speedValue, 2.00D) / (this.stationBuffer * 2.00D);
		double shutdownHalfCostValue = (int) ((halfSpeedValue / 3.60D) / startupAccelerationValue);
		Double shutdownHalfCost = Double.valueOf(shutdownHalfCostValue / 60.00D);
		double shutdownAccelerationValue = -startupAccelerationValue;
		Double startupAcceleration = Double.valueOf(startupAccelerationValue);
		Double shutdownAcceleration = Double.valueOf(shutdownAccelerationValue);
		double safeDistanceValue = aircraft.getSafeDistance();
		Double safeDistance = Double.valueOf(safeDistanceValue);
		double lastDistance = 0.00D;
		double lastCost = 0.00D;
		double verticalCostValue = (int) (startVerticalDistanceValue / speedValue);
		double verticalCostValue4Minute = verticalCostValue / 60.00D;
		double distance = 0.00D;
		double cost = 0.00D;
		Double verticalCost4Minute = Double.valueOf(verticalCostValue4Minute);
		String lastId = null;
		StringBuilder id = new StringBuilder();
		String currId = String.valueOf(id.append(System.currentTimeMillis())
				.append(this.random.nextInt(Integer.MAX_VALUE)).toString().hashCode());
		id.delete(0, id.length());

		long startTime = task.getStartTime().getTime();
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(startTime);
		List<Point> coordinates = new ArrayList<Point>(coordinateNum);
		BulkRequest bulkRequest = this.elasticsearchDao.generateBulkRequest();
		while (i + 1 < coordinateNum) {
			JSONArray current = track.getJSONArray(i);
			double currLon = current.getDoubleValue(0);
			double currLat = current.getDoubleValue(1);
			JSONArray next = track.getJSONArray(++i);
			double nextLon = next.getDoubleValue(0);
			double nextLat = next.getDoubleValue(1);
			double tmpDistance = SpatialUtil.computeDistance(currLon, currLat, nextLon, nextLat);
			Map<String, Double> coordinate = new HashMap<String, Double>();
			coordinate.put(CommonFields.LON.getName(), Double.valueOf(currLon));
			coordinate.put(CommonFields.LAT.getName(), Double.valueOf(currLat));
			Map<String, Object> data = new HashMap<String, Object>();
			data.put(CommonFields.POSITION.getName(), coordinate);
			data.put(RouteComponentFields.INDEX.getName(),i);
			data.put(RouteComponentFields.POSITION_SHAPE.getName(),
					GeoJsonToMapConverter.INSTANCE.convert(GeoJsonPoint.of(currLon, currLat)));
			data.put(CommonFields.ALTITUDE.getName(), maxAltitude);
			if (this.startupAndShutdownMode) {
				if (distance <= launchBuffer) {
					long tmpTime = calendar.getTimeInMillis();
					calendar.setTimeInMillis(startTime);
					double horizontalDistance = 0.00D;
					if (distance > 0.00D) {
						calendar.add(Calendar.SECOND, (int) verticalCostValue);
						horizontalDistance = distance - startVerticalDistanceValue;
					}
					double tmpCost = Math.sqrt((horizontalDistance * 2.00D) / startupAccelerationValue);
					calendar.add(Calendar.SECOND, (int) tmpCost);
					long currTime = calendar.getTimeInMillis();
					lastCost = (currTime - tmpTime) / 60000.00D;
					double futureDistance = horizontalDistance + tmpDistance;
					if (futureDistance <= this.stationBuffer) {
						calendar.setTimeInMillis(startTime);
						if (distance > 0.00D) {
							calendar.add(Calendar.SECOND, (int) verticalCostValue);
						}
						calendar.add(Calendar.SECOND,
								(int) (Math.sqrt((futureDistance * 2.00D) / startupAccelerationValue)));
						data.put(RouteComponentFields.NEXT_POSITION_COST.getName(),
								Double.valueOf((calendar.getTimeInMillis() - currTime) / 60000.00D));
						calendar.setTimeInMillis(currTime);
					} else {
						data.put(RouteComponentFields.NEXT_POSITION_COST.getName(), Double.valueOf(
								this.computeCost(tmpDistance, startupAccelerationValue, speedValue, tmpCost) / 60.00D));
					}
					data.put(RouteComponentFields.SPEED.getName(),
							Double.valueOf(startupAccelerationValue * tmpCost * 3.60D));
					data.put(RouteComponentFields.ACCELERATION.getName(), startupAcceleration);
				} else if (i + 2 >= coordinateNum) {
					if (i + 2 == coordinateNum) {
						lastCost = (int) (lastDistance / speedValue);
						data.put(RouteComponentFields.SPEED.getName(), speed);
					} else {
						lastCost = shutdownHalfCostValue;
						data.put(RouteComponentFields.SPEED.getName(), halfSpeed);
					}
					calendar.add(Calendar.SECOND, (int) lastCost);
					lastCost = lastCost / 60.00D;
					data.put(RouteComponentFields.ACCELERATION.getName(), shutdownAcceleration);
					data.put(RouteComponentFields.NEXT_POSITION_COST.getName(), shutdownHalfCost);
				} else {
					double elapsedDistance = distance - lastDistance;
					if (elapsedDistance <= this.stationBuffer) {
						lastCost = this.computeCost(lastDistance, startupAccelerationValue, speedValue,
								Math.sqrt((elapsedDistance * 2.00D) / startupAccelerationValue));
					} else {
						lastCost = (int) (lastDistance / speedValue);
					}
					calendar.add(Calendar.SECOND, (int) lastCost);
					lastCost = lastCost / 60.00D;
					data.put(RouteComponentFields.SPEED.getName(), speed);
					data.put(RouteComponentFields.ACCELERATION.getName(), Constant.DOUBLE_ZERO);
					data.put(RouteComponentFields.NEXT_POSITION_COST.getName(),
							Double.valueOf(((int) (tmpDistance / speedValue)) / 60.00D));
				}
			} else {
				lastCost = (int) (lastDistance / speedValue);
				calendar.add(Calendar.SECOND, (int) lastCost);
				lastCost = lastCost / 60.00D;
				data.put(RouteComponentFields.SPEED.getName(), speed);
				data.put(RouteComponentFields.ACCELERATION.getName(), Constant.DOUBLE_ZERO);
				data.put(RouteComponentFields.NEXT_POSITION_COST.getName(),
						Double.valueOf(((int) (tmpDistance / speedValue)) / 60.00D));
			}
			data.put(RouteComponentFields.OWN_TIME.getName(), Long.valueOf(calendar.getTimeInMillis()));
			data.put(CommonFields.TASK_ID.getName(), taskId);
			data.put(CommonFields.ALTITUDE.getName(), altitude);
			this.setSignalInfos(currLon, currLat, safeDistanceValue, data);
			data.put(RouteComponentFields.SAFE_DISTANCE.getName(), safeDistance);
			String nextId = String.valueOf(id.append(System.currentTimeMillis())
					.append(this.random.nextInt(Integer.MAX_VALUE)).toString().hashCode());
			id.delete(0, id.length());
			data.put(RouteComponentFields.NEXT_POSITION_ID.getName(), nextId);
			data.put(RouteComponentFields.NEXT_POSITION_DISTANCE.getName(), Double.valueOf(tmpDistance));
			if (distance > 0.00D) {
				data.put(RouteComponentFields.LAST_POSITION_ID.getName(), lastId);
			}
			data.put(RouteComponentFields.LAST_POSITION_DISTANCE.getName(), Double.valueOf(lastDistance));
			data.put(RouteComponentFields.LAST_POSITION_COST.getName(), Double.valueOf(lastCost));
			data.put(RouteComponentFields.REMAINING.getName(), Integer.valueOf(remaining--));
			if (Integer.MIN_VALUE == validLastLon || Integer.MIN_VALUE == validLastLat
					|| SpatialUtil.computeCosine(validLastLon, validLastLat, currLon, currLat, nextLon,
					nextLat) < extractAngleCosine) {
				data.put(RouteComponentFields.IS_PIVOTAL.getName(), Boolean.TRUE);
				validLastLon = currLon;
				validLastLat = currLat;
			} else {
				data.put(RouteComponentFields.IS_PIVOTAL.getName(), Boolean.FALSE);
			}
			if (i == 1) {
				String startId = String.valueOf(id.append(System.currentTimeMillis())
						.append(this.random.nextInt(Integer.MAX_VALUE)).toString().hashCode());
				id.delete(0, id.length());
				Map<String, Object> start = new HashMap<String, Object>(data);
				start.put(CommonFields.ALTITUDE.getName(), Double.valueOf(startAltitude));
				start.put(RouteComponentFields.NEXT_POSITION_ID.getName(), currId);
				start.put(RouteComponentFields.NEXT_POSITION_DISTANCE.getName(), startVerticalDistance);
				start.put(RouteComponentFields.NEXT_POSITION_COST.getName(), verticalCost4Minute);
				bulkRequest.add(this.elasticsearchDao.buildIndexRequest(ElasticsearchIndices.ROUTE_COMPONENT_INDEX,
						startId, start, false));
				calendar.add(Calendar.SECOND, (int) verticalCostValue);
				data.put(RouteComponentFields.OWN_TIME.getName(), Long.valueOf(calendar.getTimeInMillis()));
				data.put(RouteComponentFields.LAST_POSITION_ID.getName(), startId);
				data.put(RouteComponentFields.LAST_POSITION_DISTANCE.getName(), startVerticalDistance);
				data.put(RouteComponentFields.LAST_POSITION_COST.getName(), verticalCost4Minute);
				data.put(RouteComponentFields.REMAINING.getName(), Integer.valueOf(remaining--));
				distance = distance + startVerticalDistanceValue;
				cost = cost + verticalCostValue4Minute;
			}
			bulkRequest.add(this.elasticsearchDao.buildIndexRequest(ElasticsearchIndices.ROUTE_COMPONENT_INDEX, currId,
					data, false));
			lastDistance = tmpDistance;
			distance = distance + lastDistance;
			cost = cost + lastCost;
			lastId = currId;
			currId = nextId;
			coordinates.add(new Point(currLon, currLat));
			if (i + 1 == coordinateNum) {
				coordinate = new HashMap<String, Double>();
				coordinate.put(CommonFields.LON.getName(), Double.valueOf(nextLon));
				coordinate.put(CommonFields.LAT.getName(), Double.valueOf(nextLat));
				data.put(CommonFields.POSITION.getName(), coordinate);
				data.put(RouteComponentFields.POSITION_SHAPE.getName(),
						GeoJsonToMapConverter.INSTANCE.convert(GeoJsonPoint.of(nextLon, nextLat)));
				lastCost = ((Double) data.get(RouteComponentFields.NEXT_POSITION_COST.getName())).doubleValue();
				calendar.add(Calendar.SECOND, (int) (lastCost * 60.00D));
				data.put(RouteComponentFields.OWN_TIME.getName(), Long.valueOf(calendar.getTimeInMillis()));
				data.put(RouteComponentFields.SPEED.getName(), Constant.DOUBLE_ZERO);
				data.put(RouteComponentFields.ACCELERATION.getName(), Constant.DOUBLE_ZERO);
				this.setSignalInfos(nextLon, nextLat, safeDistanceValue, data);
				data.remove(RouteComponentFields.NEXT_POSITION_ID.getName());
				data.put(RouteComponentFields.NEXT_POSITION_DISTANCE.getName(), Constant.DOUBLE_ZERO);
				data.put(RouteComponentFields.NEXT_POSITION_COST.getName(), Constant.DOUBLE_ZERO);
				data.put(RouteComponentFields.LAST_POSITION_ID.getName(), lastId);
				data.put(RouteComponentFields.LAST_POSITION_DISTANCE.getName(), Double.valueOf(tmpDistance));
				data.put(RouteComponentFields.LAST_POSITION_COST.getName(), Double.valueOf(lastCost));
				data.put(RouteComponentFields.REMAINING.getName(), Integer.valueOf(remaining--));
				data.put(RouteComponentFields.IS_PIVOTAL.getName(), Boolean.TRUE);
				double endVerticalDistanceValue = altitudeValue - endAltitude;
				verticalCostValue = (int) (endVerticalDistanceValue / speedValue);
				verticalCostValue4Minute = verticalCostValue / 60.00D;
				verticalCost4Minute = Double.valueOf(verticalCostValue4Minute);
				Double endVerticalDistance = Double.valueOf(endVerticalDistanceValue);
				String endId = String.valueOf(id.append(System.currentTimeMillis())
						.append(this.random.nextInt(Integer.MAX_VALUE)).toString().hashCode());
				id.delete(0, id.length());
				Map<String, Object> end = new HashMap<String, Object>(data);
				calendar.add(Calendar.SECOND, (int) verticalCostValue);
				end.put(RouteComponentFields.OWN_TIME.getName(), Long.valueOf(calendar.getTimeInMillis()));
				end.put(CommonFields.ALTITUDE.getName(), Double.valueOf(endAltitude));
				end.put(RouteComponentFields.LAST_POSITION_ID.getName(), currId);
				end.put(RouteComponentFields.LAST_POSITION_DISTANCE.getName(), endVerticalDistance);
				end.put(RouteComponentFields.LAST_POSITION_COST.getName(), verticalCost4Minute);
				end.put(RouteComponentFields.REMAINING.getName(), Integer.valueOf(remaining));
				bulkRequest.add(this.elasticsearchDao.buildIndexRequest(ElasticsearchIndices.ROUTE_COMPONENT_INDEX,
						endId, end, false));
				data.put(RouteComponentFields.NEXT_POSITION_ID.getName(), endId);
				data.put(RouteComponentFields.NEXT_POSITION_DISTANCE.getName(), endVerticalDistance);
				data.put(RouteComponentFields.NEXT_POSITION_COST.getName(), verticalCost4Minute);
				bulkRequest.add(this.elasticsearchDao.buildIndexRequest(ElasticsearchIndices.ROUTE_COMPONENT_INDEX,
						currId, data, false));

				GetOldPointTool.getPoints(bulkRequest, data1,task);
				this.elasticsearchDao.persistence(bulkRequest);
				distance = distance + endVerticalDistanceValue;
				cost = cost + verticalCostValue4Minute;
				cost = cost + lastCost;
				coordinates.add(new Point(nextLon, nextLat));
				data = new HashMap<String, Object>();
				data.put(RouteIntegrationFields.TRACK.getName(),
						GeoJsonToMapConverter.INSTANCE.convert(GeoJsonLineString.of(coordinates)));
				data.put(CommonFields.ALTITUDE.getName(), altitude);
				data.put(CommonFields.TASK_ID.getName(), taskId);
				data.put(RouteIntegrationFields.START_TIME.getName(), Long.valueOf(startTime));
				this.elasticsearchDao.save(ElasticsearchIndices.ROUTE_INTEGRATION_INDEX, taskId, data);
			}
		}
		if(isUpdateDb) {
			this.taskDao.updateTask(taskIdValue, distance, cost);
		}
		return cost;
	}

	public void setSignalInfos(double lon, double lat, double safeDistance, Map<String, Object> data) {
		Signal signal = this.analyseSignal(lon, lat, getAllBaseStations());
		//通信信号
		if (signal != null) {
			double positioningAccuracy = safeDistance;
			Map<BaseStationTypes, SignalStrengths> signalTypesStrength = signal.getTypesStrength();
			Map<BaseStationTypes, Integer> signalTypesAccuracy = signal.getTypesAccuracy();
			Map<BaseStationTypes, Integer> signalTypesNum = signal.getTypesNum();
			Map<BaseStationTypes, double[]> signalTypesPosition = signal.getSignalTypesPosition();
			SignalStrengths signalStrength = signalTypesStrength.get(BaseStationTypes.COMMUNICATION);
			if (signalStrength != null) {
				positioningAccuracy = signalTypesAccuracy.get(BaseStationTypes.COMMUNICATION).intValue()
						+ positioningAccuracy;
				data.put(RouteComponentFields.COMMUNICATION_SIGNAL.getName(), Double.valueOf(signalStrength.getCode()));
				data.put(RouteComponentFields.COMMUNICATION_SIGNAL_NUM.getName(),
						Integer.valueOf(signalTypesNum.get(BaseStationTypes.COMMUNICATION).intValue()));
				int i = 0;
				double[] positionWithDistance = signalTypesPosition.get(BaseStationTypes.COMMUNICATION);
				List<Double> position = new ArrayList<Double>(2);
				position.add(Double.valueOf(positionWithDistance[i++]));
				position.add(Double.valueOf(positionWithDistance[i]));
				data.put(RouteComponentFields.COMMUNICATION_SIGNAL_POSITION.getName(), position);
			} else {
				positioningAccuracy = RouteService.NONE_SIGNAL_CONFIG.get(BaseStationTypes.COMMUNICATION).intValue()
						+ positioningAccuracy;
				data.put(RouteComponentFields.COMMUNICATION_SIGNAL_NUM.getName(), Integer.valueOf(0));
			}

			//导航信号
			signalStrength = signalTypesStrength.get(BaseStationTypes.NAVIGATION);
			if (signalStrength != null) {
				positioningAccuracy = signalTypesAccuracy.get(BaseStationTypes.NAVIGATION).intValue()
						+ positioningAccuracy;
				data.put(RouteComponentFields.NAVIGATION_SIGNAL.getName(), Double.valueOf(signalStrength.getCode()));
				data.put(RouteComponentFields.NAVIGATION_SIGNAL_NUM.getName(),
						Integer.valueOf(signalTypesNum.get(BaseStationTypes.NAVIGATION).intValue()));
				int i = 0;
				double[] positionWithDistance = signalTypesPosition.get(BaseStationTypes.NAVIGATION);
				List<Double> position = new ArrayList<Double>(2);
				position.add(Double.valueOf(positionWithDistance[i++]));
				position.add(Double.valueOf(positionWithDistance[i]));
				data.put(RouteComponentFields.NAVIGATION_SIGNAL_POSITION.getName(), position);
			} else {
				positioningAccuracy = RouteService.NONE_SIGNAL_CONFIG.get(BaseStationTypes.NAVIGATION).intValue()
						+ positioningAccuracy;
				data.put(RouteComponentFields.NAVIGATION_SIGNAL_NUM.getName(), Integer.valueOf(0));
			}

			//监督信号处理
			signalStrength = signalTypesStrength.get(BaseStationTypes.SUPERVISORY);
			if (signalStrength != null) {
				positioningAccuracy = signalTypesAccuracy.get(BaseStationTypes.SUPERVISORY).intValue()
						+ positioningAccuracy;
				data.put(RouteComponentFields.SUPERVISORY_SIGNAL.getName(), Double.valueOf(signalStrength.getCode()));
				data.put(RouteComponentFields.SUPERVISORY_SIGNAL_NUM.getName(),
						Integer.valueOf(signalTypesNum.get(BaseStationTypes.SUPERVISORY).intValue()));
				int i = 0;
				double[] positionWithDistance = signalTypesPosition.get(BaseStationTypes.SUPERVISORY);
				List<Double> position = new ArrayList<Double>(2);
				position.add(Double.valueOf(positionWithDistance[i++]));
				position.add(Double.valueOf(positionWithDistance[i]));
				data.put(RouteComponentFields.SUPERVISORY_SIGNAL_POSITION.getName(), position);
			} else {
				positioningAccuracy = RouteService.NONE_SIGNAL_CONFIG.get(BaseStationTypes.SUPERVISORY).intValue()
						+ positioningAccuracy;
				data.put(RouteComponentFields.SUPERVISORY_SIGNAL_NUM.getName(), Integer.valueOf(0));
			}
			data.put(RouteComponentFields.POSITIONING_ACCURACY.getName(), Double.valueOf(positioningAccuracy));
			data.put(RouteComponentFields.CONN_NUM.getName(), Integer.valueOf(signal.getConnNum()));
		}
	}

	public List<Map<String, Object>> getAllBaseStations() {

		if(PointInfoScheduler.signalStrength == null || PointInfoScheduler.signalStrength.size()== 0) {
			QueryBuilder queryBuilder = QueryBuilders.termQuery("status", 1);
			ElasticsearchQueryResult baseStationResult = this.elasticsearchDao.query(queryBuilder, 1,
					10000,
					new String[]{CommonFields.POSITION.getName(), CommonFields.TYPE.getName(), BaseStationFields.RANGE.getName()},
					new String[]{CommonFields.NAME.getName(), CommonFields.STATUS.getName(),
							BaseStationFields.MAXRANGE.getName(), BaseStationFields.ANTETYPE.getName(),
							BaseStationFields.SECTOR.getName(), BaseStationFields.FREQUENCY.getName(),
							BaseStationFields.MINFREQ.getName(), BaseStationFields.MAXFREQ.getName(),
							BaseStationFields.ANGLE.getName(), BaseStationFields.MAXANGLE.getName()},
					ElasticsearchIndices.BASE_STATION_INDEX);
			if (ElasticsearchQueryResult.EMPTY != baseStationResult) {
				PointInfoScheduler.signalStrength= baseStationResult.getData();
				return baseStationResult.getData();
			}
			return new ArrayList<>();
		}
		return PointInfoScheduler.signalStrength;
	}

	public  Polygon getPolygon(double longitude, double latitude,double radius){
		org.locationtech.jts.geom.Point point= BufferBase.geometryFactory.createPoint(new Coordinate(longitude, latitude));
		return  (Polygon) polygonBuffer.getBuff(point,1, radius);
	}
	/**
	 * 该函数根据给定的经纬度和基站信息，分析信号强度并返回一个Signal对象。函数遍历基站列表，
	 * 计算给定点与每个基站的距离，根据距离和基站的信号强度范围，确定信号强度。然后根据基站的类型，
	 * 更新不同类型的信号强度、准确度、数量和位置信息。最后，如果存在连接的基站，则返回Signal对象，否则返回null。
	 * @param lon
	 * @param lat
	 * @param baseStations
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private Signal analyseSignal(double lon, double lat, List<Map<String, Object>> baseStations) {
		int connNum = 0;
		SignalStrengths[] signalStrengths = SignalStrengths.values();
		int strengthNum = signalStrengths.length;
		int size = baseStations.size();
		Map<BaseStationTypes, SignalStrengths> signalTypesStrength = new HashMap<BaseStationTypes, SignalStrengths>();
		Map<BaseStationTypes, Integer> signalTypesAccuracy = new HashMap<BaseStationTypes, Integer>();
		Map<BaseStationTypes, Integer> signalTypesNum = new HashMap<BaseStationTypes, Integer>();
		Map<BaseStationTypes, double[]> signalTypesPosition = new HashMap<BaseStationTypes, double[]>();
		Point2D.Double poin2D = new Point2D.Double(lon, lat);
		for (int i = 0; i < size;i++){
			Map<String, Object> baseStation = baseStations.get(i);
			Map<String, Object> pointPosition = (Map<String, Object>) baseStation.get(CommonFields.POSITION.getName());
			double pointLon = ((Double) pointPosition.get(CommonFields.LON.getName())).doubleValue();
			double pointLat = ((Double) pointPosition.get(CommonFields.LAT.getName())).doubleValue();
			double distance = SpatialUtil.computeDistance(lon, lat, pointLon, pointLat);
			double range=Double.valueOf(baseStation.get(BaseStationFields.RANGE.getName())+"");
			Polygon polygon=getPolygon(pointLon,pointLat ,range);
			Coordinate[] coordinates=polygon.getBoundary().getCoordinates();
			List<Point2D.Double> pointList = new ArrayList<Point2D.Double>();
			for(int j=0;j<coordinates.length;j++){
				Coordinate coordinate=coordinates[j];
				Point2D.Double t = new Point2D.Double(coordinate.x, coordinate.y);
				pointList.add(t);
			}
			boolean flag=GeoUtil.isInPolygon(poin2D,pointList );
			int j = 0;
			// 如果距离大于范围，则跳过
			if(! flag){
				//没有信号
				j=strengthNum+100;
			}else {
				double distanceRatio=(distance/range)*100;
				if(distanceRatio<=33.33){
					//弱信号
					j=0;
				}else if (distanceRatio<=66.66){
					//中信号
					j=1;
				}else if (distanceRatio<=100 || distanceRatio>100){
					//强信号
					j=2;
				}
			}
			if (j < strengthNum) {
				++connNum;
				SignalStrengths signalStrength = signalStrengths[j];
				int type = ((Integer) baseStation.get(CommonFields.TYPE.getName())).intValue();
				if (BaseStationTypes.COMMUNICATION.getCode() == type) {
					this.updateSignal(BaseStationTypes.COMMUNICATION, signalStrength, signalTypesStrength,
							signalTypesAccuracy, signalTypesNum, distance, new double[] { pointLon, pointLat },
							signalTypesPosition);
				} else if (BaseStationTypes.NAVIGATION.getCode() == type) {
					this.updateSignal(BaseStationTypes.NAVIGATION, signalStrength, signalTypesStrength,
							signalTypesAccuracy, signalTypesNum, distance, new double[] { pointLon, pointLat },
							signalTypesPosition);
				} else if (BaseStationTypes.SUPERVISORY.getCode() == type) {
					this.updateSignal(BaseStationTypes.SUPERVISORY, signalStrength, signalTypesStrength,
							signalTypesAccuracy, signalTypesNum, distance, new double[] { pointLon, pointLat },
							signalTypesPosition);
				}
			}
		}
		if (connNum > 0) {
			return new Signal(connNum, signalTypesStrength, signalTypesAccuracy, signalTypesNum, signalTypesPosition);
		}
		return null;
	}



	private void updateSignal(BaseStationTypes baseStationType, SignalStrengths signalStrength,
							  Map<BaseStationTypes, SignalStrengths> signalTypesStrength,
							  Map<BaseStationTypes, Integer> signalTypesAccuracy, Map<BaseStationTypes, Integer> signalTypesNum,
							  double distance, double[] position, Map<BaseStationTypes, double[]> signalTypesPosition) {
		SignalStrengths tmpSignalStrength = signalTypesStrength.get(baseStationType);
		if (tmpSignalStrength == null || tmpSignalStrength.getCode() > signalStrength.getCode()) {
			signalTypesStrength.put(baseStationType, signalStrength);
			signalTypesAccuracy.put(baseStationType,
					RouteService.SIGNAL_CONFIG.get(signalStrength).get(baseStationType));
		}
		Integer num = signalTypesNum.get(baseStationType);
		if (num == null) {
			signalTypesNum.put(baseStationType, Integer.valueOf(1));
		} else {
			signalTypesNum.put(baseStationType, Integer.valueOf(num.intValue() + 1));
		}
		double[] positionWithDistance = signalTypesPosition.get(baseStationType);
		if (positionWithDistance == null) {
			int i = 0;
			signalTypesPosition.put(baseStationType, new double[] { position[i++], position[i], distance });
		} else if (positionWithDistance[positionWithDistance.length - 1] > distance) {
			int i = 0;
			positionWithDistance[i] = position[i++];
			positionWithDistance[i] = position[i++];
			positionWithDistance[i] = distance;
		}
	}

	public static class RouteResultWithAircraftPositions {

		private JSONObject routeResult = null;

		private List<Map<String, Object>> aircraftPositions = null;

		public RouteResultWithAircraftPositions(JSONObject routeResult, List<Map<String, Object>> aircraftPositions) {
			this.routeResult = routeResult;
			this.aircraftPositions = aircraftPositions;
		}

		public JSONObject getRouteResult() {
			return routeResult;
		}

	}

	private RouteResultWithAircraftPositions smartRoute(double minAltitude, double maxAltitude, double safeDistance,
														double[] startCoordinate, JSONArray settings, int mode) {
		int i = 0;
		int j = 0;
		int size = settings.size();
		double[][] points = new double[size + 1][];
		points[i++] = startCoordinate;
		while (i <= size) {
			JSONArray coordinate = settings.getJSONObject(j++).getJSONArray("position");
			points[i++] = new double[] { coordinate.getDoubleValue(0), coordinate.getDoubleValue(1) };
		}
		i = 0;
		Map<String, Object> params = new HashMap<String, Object>();
		RouteParams[] routeParams = RouteParams.values();
		size = routeParams.length;
		while (i < size) {
			RouteParams routeParam = routeParams[i++];
			Object defaultValue = routeParam.getDefaultValue();
			if (defaultValue != null) {
				params.put(routeParam.getName(), defaultValue);
			}
		}
		params.put(RouteParams.FLIGHT_TYPE.getName(), String.valueOf(mode));
		params.put(RouteParams.MIN_HEIGHT.getName(), String.valueOf((int) minAltitude));
		params.put(RouteParams.MAX_HEIGHT.getName(), String.valueOf((int) maxAltitude));
		params.put(RouteParams.POINTS.getName(), points);
		if (FlyModes.ROAD_NET.getCode() == mode) {
			return new RouteResultWithAircraftPositions(HttpUtil.postRequest(this.roadNetRouteUrl, params), null);
		}
		List<Map<String, Object>> aircraftPositions = null;
		if (FlyModes.FREE.getCode() == mode) {
			List<Polygon> stationAreas = this.generateStationAreas(minAltitude, safeDistance, points);
			AircraftAreas aircraftAreas = this.aircraftIsObstacle
					? this.generateAircraftAreas(minAltitude, maxAltitude, safeDistance)
					: null;
			List<Polygon> obstacleAreas = null;
			if (aircraftAreas != null) {
				aircraftPositions = aircraftAreas.getPositions();
				obstacleAreas = aircraftAreas.getAreas();
			}
			if (obstacleAreas == null) {
				obstacleAreas = stationAreas;
			} else {
				List<Polygon> polygons = new ArrayList<Polygon>();
				this.unionPolygons(stationAreas, polygons);
				this.unionPolygons(obstacleAreas, polygons);
				obstacleAreas = polygons;
			}
			if (this.enableNoFlyArea) {
				List<Polygon> noFlyAreas = this.getNoFlyAreas();
				if (noFlyAreas != null) {
					if (obstacleAreas != null) {
						List<Polygon> polygons = new ArrayList<Polygon>();
						this.unionPolygons(noFlyAreas, polygons);
						this.unionPolygons(obstacleAreas, polygons);
						obstacleAreas = polygons;
					} else {
						obstacleAreas = noFlyAreas;
					}
				}
			}
			if (obstacleAreas != null) {
				params.put(RouteParams.OBSTACLE_VOS.getName(), this.buildObstacleAreas(obstacleAreas));
			}
		}
		return new RouteResultWithAircraftPositions(HttpUtil.postRequest(this.airRouteUrl, params), aircraftPositions);
	}

	public void setEnableNoFlyArea(boolean enableNoFlyArea) {
		this.enableNoFlyArea = enableNoFlyArea;
	}

	private void unionPolygons(List<Polygon> src, List<Polygon> dest) {
		int i = 0;
		int size = src.size();
		while (i < size) {
			this.unionPolygon(src.get(i++), dest);
		}
	}

	public List<ObstacleArea> buildObstacleAreas(List<Polygon> src) {
		int i = 0;
		int size = src.size();
		List<ObstacleArea> obstacleAreas = new ArrayList<ObstacleArea>(size);
		while (i < size) {
			int j = 0;
			Coordinate[] coordinates = src.get(i++).getCoordinates();
			int length = coordinates.length;
			Coordinate coordinate = coordinates[j++];
			StringBuilder points = new StringBuilder();
			points.append(coordinate.x).append(Constant.COMMA).append(coordinate.y);
			while (j < length) {
				coordinate = coordinates[j++];
				points.append(Constant.SEMICOLON).append(coordinate.x).append(Constant.COMMA).append(coordinate.y);
			}
			obstacleAreas.add(new ObstacleArea(points.toString()));
		}
		return obstacleAreas;
	}

	private List<Polygon> generateStationAreas(double minAltitude, double safeDistance, double[][] points) {
		StationQueryResult positionResult = this.stationDao.queryValidStation(minAltitude - safeDistance);
		if (StationQueryResult.EMPTY != positionResult) {
			return this.generateStationPolygons(positionResult.getData(), this.stationBuffer, points);
		}
		return null;
	}

	private List<Polygon> generateStationPolygons(List<double[]> positions, double safeDistance, double[][] points) {
		safeDistance = safeDistance / 1000.00D;
		int i = 0;
		int size = positions.size();
		List<Polygon> polygons = new ArrayList<Polygon>();
		while (i < size) {
			int j = 0;
			double[] position = positions.get(i++);
			if (position != null) {
				double lon = position[j++];
				double lat = position[j];
				if (!this.isFilter(lon, lat, points)) {
					this.unionPolygon(this.generateCircle(lon, lat, safeDistance), polygons);
				}
			}
		}
		if (polygons.size() == 0) {
			return null;
		}
		return polygons;
	}

	private List<Polygon> getNoFlyAreas() {
		ElasticsearchQueryResult borderResult = this.elasticsearchDao.query(QueryBuilders.matchAllQuery(), 1, 10000,
				new String[] { AreaFields.BORDER.getName() }, new String[0], ElasticsearchIndices.NO_FLY_AREA_INDEX);
		if (ElasticsearchQueryResult.EMPTY != borderResult) {
			return this.generatePolygons(borderResult.getData());
		}
		return null;
	}

	@SuppressWarnings({ "unchecked" })
	private List<Polygon> generatePolygons(List<Map<String, Object>> borders) {
		int i = 0;
		int size = borders.size();
		List<Polygon> polygons = new ArrayList<Polygon>();
		while (i < size) {
			try {
				this.unionPolygon((Polygon) new GeometryJSON().read(GeoJSONUtil.toReader(
								JSON.toJSONString((Map<String, Object>) borders.get(i++).get(AreaFields.BORDER.getName())))),
						polygons);
			} catch (Throwable e) {
				RouteService.log.error(e.getMessage(), e);
			}
		}
		if (polygons.size() == 0) {
			return null;
		}
		return polygons;
	}

	private void unionPolygon(Polygon polygon, List<Polygon> polygons) {
		int i = 0;
		int size = polygons.size();
		List<Integer> intersectIndices = new ArrayList<Integer>();
		while (i < size) {
			if (polygons.get(i).intersects(polygon)) {
				intersectIndices.add(Integer.valueOf(i));
			}
			++i;
		}
		size = intersectIndices.size();
		if (size > 0) {
			while (--size >= 0) {
				polygon = (Polygon) polygon.union(polygons.remove(intersectIndices.remove(size).intValue()));
			}
		}
		polygons.add(polygon);
	}

	private BoolQueryBuilder generateTaskIdsQuery(List<TaskModel> tasks) {
		int i = 0;
		int size = tasks.size();
		BoolQueryBuilder query = QueryBuilders.boolQuery();
		while (i < size) {
			query.should(
					QueryBuilders.termQuery(CommonFields.TASK_ID.getName(), String.valueOf(tasks.get(i++).getId())));
		}
		return query;
	}

	private class AircraftAreas {

		private List<Map<String, Object>> positions = null;

		private List<Polygon> areas = null;

		public AircraftAreas(List<Map<String, Object>> positions, List<Polygon> areas) {
			this.positions = positions;
			this.areas = areas;
		}

		public List<Map<String, Object>> getPositions() {
			return positions;
		}

		public List<Polygon> getAreas() {
			return areas;
		}
	}

	private AircraftAreas generateAircraftAreas(double minAltitude, double maxAltitude, double safeDistance) {
		TaskQueryResult taskResult = this.taskDao.queryFreeModeTask();
		if (TaskQueryResult.EMPTY == taskResult) {
			return null;
		}
		BoolQueryBuilder query = QueryBuilders.boolQuery();
		query.filter(this.generateTaskIdsQuery(taskResult.getData()));
		query.filter(QueryBuilders.rangeQuery(CommonFields.ALTITUDE.getName()).gte(Double.valueOf(minAltitude))
				.lte(Double.valueOf(maxAltitude)));
		query.filter(this.generateRealTimeQuery(null, this.systemTimeBuffer));
		ElasticsearchQueryResult positionResult = this.elasticsearchDao.query(query, 1, 10000,
				SortBuilders.fieldSort(RouteComponentFields.OWN_TIME.getName()).order(SortOrder.ASC),
				new String[] { CommonFields.POSITION.getName() },
				new String[] { RouteComponentFields.OWN_TIME.getName(), CommonFields.TASK_ID.getName(),
						RouteComponentFields.SPEED.getName(), RouteComponentFields.ACCELERATION.getName(),
						CommonFields.ALTITUDE.getName(), RouteComponentFields.COMMUNICATION_SIGNAL.getName(),
						RouteComponentFields.NAVIGATION_SIGNAL.getName(),
						RouteComponentFields.SUPERVISORY_SIGNAL.getName(), RouteComponentFields.SAFE_DISTANCE.getName(),
						RouteComponentFields.POSITIONING_ACCURACY.getName(), RouteComponentFields.CONN_NUM.getName(),
						RouteComponentFields.NEXT_POSITION_ID.getName(),
						RouteComponentFields.NEXT_POSITION_DISTANCE.getName(),
						RouteComponentFields.NEXT_POSITION_COST.getName(),
						RouteComponentFields.LAST_POSITION_ID.getName(),
						RouteComponentFields.LAST_POSITION_DISTANCE.getName(),
						RouteComponentFields.LAST_POSITION_COST.getName(), RouteComponentFields.REMAINING.getName(),
						RouteComponentFields.COMMUNICATION_SIGNAL_NUM.getName(),
						RouteComponentFields.NAVIGATION_SIGNAL_NUM.getName(),
						RouteComponentFields.SUPERVISORY_SIGNAL_NUM.getName(),
						RouteComponentFields.CONFLICT_TASK_ID.getName(), RouteComponentFields.POSITION_SHAPE.getName(),
						RouteComponentFields.IS_PIVOTAL.getName(), CommonFields.STATUS.getName(),
						RouteComponentFields.RETARD_DISTANCE.getName(),
						RouteComponentFields.INTERSECTION_DISTANCE.getName(), RouteComponentFields.HOVER_COST.getName(),
						RouteComponentFields.COMMUNICATION_SIGNAL_POSITION.getName(),
						RouteComponentFields.NAVIGATION_SIGNAL_POSITION.getName(),
						RouteComponentFields.SUPERVISORY_SIGNAL_POSITION.getName() },
				new CollapseBuilder(CommonFields.TASK_ID.getName()), ElasticsearchIndices.ROUTE_COMPONENT_INDEX);
		if (ElasticsearchQueryResult.EMPTY != positionResult) {
			List<Map<String, Object>> positions = positionResult.getData();
			return new AircraftAreas(positions, this.generatePolygons(positions, safeDistance, null));
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	private List<Polygon> generatePolygons(List<Map<String, Object>> positions, double safeDistance,
										   double[][] points) {
		safeDistance = safeDistance / 1000.00D;
		int i = 0;
		int size = positions.size();
		List<Polygon> polygons = new ArrayList<Polygon>();
		while (i < size) {
			Map<String, Object> position = (Map<String, Object>) positions.get(i++)
					.get(CommonFields.POSITION.getName());
			double lon = ((Double) position.get(CommonFields.LON.getName())).doubleValue();
			double lat = ((Double) position.get(CommonFields.LAT.getName())).doubleValue();
			if (!this.isFilter(lon, lat, points)) {
				this.unionPolygon(this.generateCircle(lon, lat, safeDistance), polygons);
			}
		}
		if (polygons.size() == 0) {
			return null;
		}
		return polygons;
	}

	private boolean isFilter(double lon, double lat, double[][] points) {
		if (points != null) {
			int size = points.length;
			if (size > 0) {
				int i = 0;
				while (i < size) {
					int j = 0;
					double[] point = points[i++];
					if (point[j++] == lon && point[j] == lat) {
						return true;
					}
				}
			}
		}
		return false;
	}

	private Polygon generateCircle(double centreLon, double centreLat, double radiusKM) {
		GeometricShapeFactory shapeFactory = new GeometricShapeFactory();
		shapeFactory.setNumPoints(this.circleEdgeNum);
		shapeFactory.setCentre(new Coordinate(centreLon, centreLat));
		shapeFactory.setSize(radiusKM / (Math.PI * this.earthRadius) * 360.00D);
		return shapeFactory.createCircle();
	}




	private double computeCost(double acceleration, double initSpeed, double finalSpeed) {
		return (finalSpeed - initSpeed) / acceleration;
	}











	private RangeQueryBuilder generateRealTimeQuery(Long time, int timeBuffer) {
		Calendar calendar = Calendar.getInstance();
		if (time != null) {
			calendar.setTimeInMillis(time.longValue());
			calendar.add(Calendar.SECOND, (int) (-(timeBuffer / 2.00D)));
		} else {
			calendar.setTimeInMillis(System.currentTimeMillis());
		}
		RangeQueryBuilder query = QueryBuilders.rangeQuery(RouteComponentFields.OWN_TIME.getName()).gte(Long.valueOf(calendar.getTimeInMillis()));
		calendar.add(Calendar.SECOND, timeBuffer);
		query.lte(Long.valueOf(calendar.getTimeInMillis()));
		return query;
	}
	public List<PreviewTrack> preview(double minAltitude, double maxAltitude, double safeDistance, double speed, JSONArray settings) {
		speed = speed / 3.60D;
		int i = 0;
		List<PreviewTrack> tracks = new ArrayList<PreviewTrack>();
		JSONArray startPoint = ((JSONObject) settings.remove(0)).getJSONArray("position");
		double startAltitude = startPoint.getDoubleValue(2);
		double altitude = startAltitude;
		double endAltitude = settings.getJSONObject(settings.size() - 1).getJSONArray("position").getDoubleValue(2);
		if (altitude < endAltitude) {
			altitude = endAltitude;
		}
		if (this.altitudeNeedBase) {
			minAltitude = minAltitude + altitude;
			maxAltitude = maxAltitude + altitude;
		}
		double[] startCoordinate = this.position2Array(startPoint);
		FlyModes[] flyModes = FlyModes.values();
		int size = flyModes.length - 1;
		while (i < size) {
			int flyMode = flyModes[i++].getCode();
			JSONObject routeResult = this
					.smartRoute(minAltitude, maxAltitude, safeDistance, startCoordinate, settings, flyMode)
					.getRouteResult();
			if (routeResult == null) {
				RouteService.log.error("结果为null");
			} else {
				JSONArray routes = routeResult.getJSONArray("paths");
				if (routes == null) {
					RouteService.log.error("paths为null");
				} else {
					int routeNum = routes.size();
					if (routeNum == 0) {
						RouteService.log.error("path数量为0");
					} else {
						JSONObject route = this.pickRoute(altitude + safeDistance, Integer.MAX_VALUE, routeNum, routes);
						if (route == null) {
							RouteService.log.error("无适当高度的path");
						} else {
							JSONObject track = route.getJSONObject("points");
							if (track == null) {
								RouteService.log.error("points为null");
							} else {
								JSONArray trackCoordinates = track.getJSONArray("coordinates");
								if (trackCoordinates == null) {
									RouteService.log.error("coordinates为null");
								} else {
									int trackCoordinateNum = trackCoordinates.size();
									if (trackCoordinateNum == 0) {
										RouteService.log.error("coordinate数量为0");
									} else {
										int j = 0;
										double distance = route.getDoubleValue("distance");
										minAltitude = route.getDoubleValue("min_altitude");
										maxAltitude = route.getDoubleValue("max_altitude");
										Double wrapAltitude = Double.valueOf(this.computeAltitudeMode
												? (this.random.nextInt((int) (maxAltitude - minAltitude)) + minAltitude)
												: ((maxAltitude + minAltitude) / 2.00D));
										while (j < trackCoordinateNum) {
											trackCoordinates.getJSONArray(j++).set(2, wrapAltitude);
										}
										tracks.add(new PreviewTrack(flyMode, distance, (distance / speed) / 60.00D,
												trackCoordinates));
									}
								}
							}
						}
					}
				}
			}
		}
		if (tracks.size() == 0) {
			return null;
		}
		return tracks;
	}
	public List<Map<String, Object>>  obstacleAreas (double minAltitude,double safeDistance,double maxAltitude,double[][] points,List<Polygon> obstacleAreas ){

		List<Map<String, Object>> aircraftPositions = null;
		List<Polygon> stationAreas = this.generateStationAreas(minAltitude, safeDistance, points);
		AircraftAreas aircraftAreas = this.aircraftIsObstacle
				? this.generateAircraftAreas(minAltitude, maxAltitude, safeDistance)
				: null;
		if (aircraftAreas != null) {
			aircraftPositions = aircraftAreas.getPositions();
			obstacleAreas = aircraftAreas.getAreas();
		}
		if (obstacleAreas == null) {
			obstacleAreas = stationAreas;
		} else {
			List<Polygon> polygons = new ArrayList<Polygon>();
			this.unionPolygons(stationAreas, polygons);
			this.unionPolygons(obstacleAreas, polygons);
			obstacleAreas = polygons;
		}
		if (this.enableNoFlyArea) {
			List<Polygon> noFlyAreas = this.getNoFlyAreas();
			if (noFlyAreas != null) {
				if (obstacleAreas != null) {
					List<Polygon> polygons = new ArrayList<Polygon>();
					this.unionPolygons(noFlyAreas, polygons);
					this.unionPolygons(obstacleAreas, polygons);
					obstacleAreas = polygons;
					obstacleAreas.clear();
					obstacleAreas.addAll(polygons);

				} else {
					obstacleAreas.clear();
					obstacleAreas.addAll(noFlyAreas);
				}
			}
		}
		return aircraftPositions;
	}

	public double persistenceCheck(double minAltitude, double maxAltitude, TaskModel task, AircraftModel aircraft,
							  int coordinateNum, JSONArray track, double startAltitude, double endAltitude, List<Map<String,Object>> data1) {
		int i = 0;
		int remaining = coordinateNum + 2;
		double extractAngleCosine = Math.cos(this.extractAngle * 0.0174533D);
		double validLastLon = Integer.MIN_VALUE;
		double validLastLat = Integer.MIN_VALUE;
		int taskIdValue = task.getId();
		String taskId = String.valueOf(taskIdValue);
		double speedValue = aircraft.getSpeed();
		double halfSpeedValue = speedValue / 2.00D;
		Double speed = Double.valueOf(speedValue);
		speedValue = speedValue / 3.60D;
		Double halfSpeed = Double.valueOf(halfSpeedValue);
		double altitudeValue = this.computeAltitudeMode
				? (this.random.nextInt((int) (maxAltitude - minAltitude)) + minAltitude)
				: ((maxAltitude + minAltitude) / 2.00D);
		double startVerticalDistanceValue = altitudeValue - startAltitude;
		double launchBuffer = this.stationBuffer + startVerticalDistanceValue;
		Double altitude = Double.valueOf(altitudeValue);
		Double startVerticalDistance = Double.valueOf(startVerticalDistanceValue);
		double startupAccelerationValue = Math.pow(speedValue, 2.00D) / (this.stationBuffer * 2.00D);
		double shutdownHalfCostValue = (int) ((halfSpeedValue / 3.60D) / startupAccelerationValue);
		Double shutdownHalfCost = Double.valueOf(shutdownHalfCostValue / 60.00D);
		double shutdownAccelerationValue = -startupAccelerationValue;
		Double startupAcceleration = Double.valueOf(startupAccelerationValue);
		Double shutdownAcceleration = Double.valueOf(shutdownAccelerationValue);
		double safeDistanceValue = aircraft.getSafeDistance();
		Double safeDistance = Double.valueOf(safeDistanceValue);
		double lastDistance = 0.00D;
		double lastCost = 0.00D;
		double verticalCostValue = (int) (startVerticalDistanceValue / speedValue);
		double verticalCostValue4Minute = verticalCostValue / 60.00D;
		double distance = 0.00D;
		double cost = 0.00D;
		Double verticalCost4Minute = Double.valueOf(verticalCostValue4Minute);
		String lastId = null;
		StringBuilder id = new StringBuilder();
		String currId = String.valueOf(id.append(System.currentTimeMillis())
				.append(this.random.nextInt(Integer.MAX_VALUE)).toString().hashCode());
		id.delete(0, id.length());

		long startTime = task.getStartTime().getTime();
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(startTime);
		List<Point> coordinates = new ArrayList<Point>(coordinateNum);
		while (i + 1 < coordinateNum) {
			JSONArray current = track.getJSONArray(i);
			double currLon = current.getDoubleValue(0);
			double currLat = current.getDoubleValue(1);
			JSONArray next = track.getJSONArray(++i);
			double nextLon = next.getDoubleValue(0);
			double nextLat = next.getDoubleValue(1);
			double tmpDistance = SpatialUtil.computeDistance(currLon, currLat, nextLon, nextLat);
			Map<String, Double> coordinate = new HashMap<String, Double>();
			coordinate.put(CommonFields.LON.getName(), Double.valueOf(currLon));
			coordinate.put(CommonFields.LAT.getName(), Double.valueOf(currLat));
			Map<String, Object> data = new HashMap<String, Object>();
			data.put(CommonFields.POSITION.getName(), coordinate);
			data.put(RouteComponentFields.INDEX.getName(),i);
			data.put(RouteComponentFields.POSITION_SHAPE.getName(),
					GeoJsonToMapConverter.INSTANCE.convert(GeoJsonPoint.of(currLon, currLat)));
			data.put(CommonFields.ALTITUDE.getName(), maxAltitude);
			if (this.startupAndShutdownMode) {
				if (distance <= launchBuffer) {
					long tmpTime = calendar.getTimeInMillis();
					calendar.setTimeInMillis(startTime);
					double horizontalDistance = 0.00D;
					if (distance > 0.00D) {
						calendar.add(Calendar.SECOND, (int) verticalCostValue);
						horizontalDistance = distance - startVerticalDistanceValue;
					}
					double tmpCost = Math.sqrt((horizontalDistance * 2.00D) / startupAccelerationValue);
					calendar.add(Calendar.SECOND, (int) tmpCost);
					long currTime = calendar.getTimeInMillis();
					lastCost = (currTime - tmpTime) / 60000.00D;
					double futureDistance = horizontalDistance + tmpDistance;
					if (futureDistance <= this.stationBuffer) {
						calendar.setTimeInMillis(startTime);
						if (distance > 0.00D) {
							calendar.add(Calendar.SECOND, (int) verticalCostValue);
						}
						calendar.add(Calendar.SECOND,
								(int) (Math.sqrt((futureDistance * 2.00D) / startupAccelerationValue)));
						data.put(RouteComponentFields.NEXT_POSITION_COST.getName(),
								Double.valueOf((calendar.getTimeInMillis() - currTime) / 60000.00D));
						calendar.setTimeInMillis(currTime);
					} else {
						data.put(RouteComponentFields.NEXT_POSITION_COST.getName(), Double.valueOf(
								this.computeCost(tmpDistance, startupAccelerationValue, speedValue, tmpCost) / 60.00D));
					}
					data.put(RouteComponentFields.SPEED.getName(),
							Double.valueOf(startupAccelerationValue * tmpCost * 3.60D));
					data.put(RouteComponentFields.ACCELERATION.getName(), startupAcceleration);
				} else if (i + 2 >= coordinateNum) {
					if (i + 2 == coordinateNum) {
						lastCost = (int) (lastDistance / speedValue);
						data.put(RouteComponentFields.SPEED.getName(), speed);
					} else {
						lastCost = shutdownHalfCostValue;
						data.put(RouteComponentFields.SPEED.getName(), halfSpeed);
					}
					calendar.add(Calendar.SECOND, (int) lastCost);
					lastCost = lastCost / 60.00D;
					data.put(RouteComponentFields.ACCELERATION.getName(), shutdownAcceleration);
					data.put(RouteComponentFields.NEXT_POSITION_COST.getName(), shutdownHalfCost);
				} else {
					double elapsedDistance = distance - lastDistance;
					if (elapsedDistance <= this.stationBuffer) {
						lastCost = this.computeCost(lastDistance, startupAccelerationValue, speedValue,
								Math.sqrt((elapsedDistance * 2.00D) / startupAccelerationValue));
					} else {
						lastCost = (int) (lastDistance / speedValue);
					}
					calendar.add(Calendar.SECOND, (int) lastCost);
					lastCost = lastCost / 60.00D;
					data.put(RouteComponentFields.SPEED.getName(), speed);
					data.put(RouteComponentFields.ACCELERATION.getName(), Constant.DOUBLE_ZERO);
					data.put(RouteComponentFields.NEXT_POSITION_COST.getName(),
							Double.valueOf(((int) (tmpDistance / speedValue)) / 60.00D));
				}
			} else {
				lastCost = (int) (lastDistance / speedValue);
				calendar.add(Calendar.SECOND, (int) lastCost);
				lastCost = lastCost / 60.00D;
				data.put(RouteComponentFields.SPEED.getName(), speed);
				data.put(RouteComponentFields.ACCELERATION.getName(), Constant.DOUBLE_ZERO);
				data.put(RouteComponentFields.NEXT_POSITION_COST.getName(),
						Double.valueOf(((int) (tmpDistance / speedValue)) / 60.00D));
			}
			data.put(RouteComponentFields.OWN_TIME.getName(), Long.valueOf(calendar.getTimeInMillis()));
			data.put(CommonFields.TASK_ID.getName(), taskId);
			data.put(CommonFields.ALTITUDE.getName(), altitude);
			this.setSignalInfos(currLon, currLat, safeDistanceValue, data);
			data.put(RouteComponentFields.SAFE_DISTANCE.getName(), safeDistance);
			String nextId = String.valueOf(id.append(System.currentTimeMillis())
					.append(this.random.nextInt(Integer.MAX_VALUE)).toString().hashCode());
			id.delete(0, id.length());
			data.put(RouteComponentFields.NEXT_POSITION_ID.getName(), nextId);
			data.put(RouteComponentFields.NEXT_POSITION_DISTANCE.getName(), Double.valueOf(tmpDistance));
			if (distance > 0.00D) {
				data.put(RouteComponentFields.LAST_POSITION_ID.getName(), lastId);
			}
			data.put(RouteComponentFields.LAST_POSITION_DISTANCE.getName(), Double.valueOf(lastDistance));
			data.put(RouteComponentFields.LAST_POSITION_COST.getName(), Double.valueOf(lastCost));
			data.put(RouteComponentFields.REMAINING.getName(), Integer.valueOf(remaining--));
			if (Integer.MIN_VALUE == validLastLon || Integer.MIN_VALUE == validLastLat
					|| SpatialUtil.computeCosine(validLastLon, validLastLat, currLon, currLat, nextLon,
					nextLat) < extractAngleCosine) {
				data.put(RouteComponentFields.IS_PIVOTAL.getName(), Boolean.TRUE);
				validLastLon = currLon;
				validLastLat = currLat;
			} else {
				data.put(RouteComponentFields.IS_PIVOTAL.getName(), Boolean.FALSE);
			}
			if (i == 1) {
				String startId = String.valueOf(id.append(System.currentTimeMillis())
						.append(this.random.nextInt(Integer.MAX_VALUE)).toString().hashCode());
				id.delete(0, id.length());
				Map<String, Object> start = new HashMap<String, Object>(data);
				start.put(CommonFields.ALTITUDE.getName(), Double.valueOf(startAltitude));
				start.put(RouteComponentFields.NEXT_POSITION_ID.getName(), currId);
				start.put(RouteComponentFields.NEXT_POSITION_DISTANCE.getName(), startVerticalDistance);
				start.put(RouteComponentFields.NEXT_POSITION_COST.getName(), verticalCost4Minute);
				data1.add(start);
				calendar.add(Calendar.SECOND, (int) verticalCostValue);
				data.put(RouteComponentFields.OWN_TIME.getName(), Long.valueOf(calendar.getTimeInMillis()));
				data.put(RouteComponentFields.LAST_POSITION_ID.getName(), startId);
				data.put(RouteComponentFields.LAST_POSITION_DISTANCE.getName(), startVerticalDistance);
				data.put(RouteComponentFields.LAST_POSITION_COST.getName(), verticalCost4Minute);
				data.put(RouteComponentFields.REMAINING.getName(), Integer.valueOf(remaining--));
				distance = distance + startVerticalDistanceValue;
				cost = cost + verticalCostValue4Minute;
			}
			data1.add(data);
			lastDistance = tmpDistance;
			distance = distance + lastDistance;
			cost = cost + lastCost;
			lastId = currId;
			currId = nextId;
			coordinates.add(new Point(currLon, currLat));
			if (i + 1 == coordinateNum) {
				coordinate = new HashMap<String, Double>();
				coordinate.put(CommonFields.LON.getName(), Double.valueOf(nextLon));
				coordinate.put(CommonFields.LAT.getName(), Double.valueOf(nextLat));
				data.put(CommonFields.POSITION.getName(), coordinate);
				data.put(RouteComponentFields.POSITION_SHAPE.getName(),
						GeoJsonToMapConverter.INSTANCE.convert(GeoJsonPoint.of(nextLon, nextLat)));
				lastCost = ((Double) data.get(RouteComponentFields.NEXT_POSITION_COST.getName())).doubleValue();
				calendar.add(Calendar.SECOND, (int) (lastCost * 60.00D));
				data.put(RouteComponentFields.OWN_TIME.getName(), Long.valueOf(calendar.getTimeInMillis()));
				data.put(RouteComponentFields.SPEED.getName(), Constant.DOUBLE_ZERO);
				data.put(RouteComponentFields.ACCELERATION.getName(), Constant.DOUBLE_ZERO);
				this.setSignalInfos(nextLon, nextLat, safeDistanceValue, data);
				data.remove(RouteComponentFields.NEXT_POSITION_ID.getName());
				data.put(RouteComponentFields.NEXT_POSITION_DISTANCE.getName(), Constant.DOUBLE_ZERO);
				data.put(RouteComponentFields.NEXT_POSITION_COST.getName(), Constant.DOUBLE_ZERO);
				data.put(RouteComponentFields.LAST_POSITION_ID.getName(), lastId);
				data.put(RouteComponentFields.LAST_POSITION_DISTANCE.getName(), Double.valueOf(tmpDistance));
				data.put(RouteComponentFields.LAST_POSITION_COST.getName(), Double.valueOf(lastCost));
				data.put(RouteComponentFields.REMAINING.getName(), Integer.valueOf(remaining--));
				data.put(RouteComponentFields.IS_PIVOTAL.getName(), Boolean.TRUE);
				double endVerticalDistanceValue = altitudeValue - endAltitude;
				verticalCostValue = (int) (endVerticalDistanceValue / speedValue);
				verticalCostValue4Minute = verticalCostValue / 60.00D;
				verticalCost4Minute = Double.valueOf(verticalCostValue4Minute);
				Double endVerticalDistance = Double.valueOf(endVerticalDistanceValue);
				String endId = String.valueOf(id.append(System.currentTimeMillis())
						.append(this.random.nextInt(Integer.MAX_VALUE)).toString().hashCode());
				id.delete(0, id.length());
				Map<String, Object> end = new HashMap<String, Object>(data);
				calendar.add(Calendar.SECOND, (int) verticalCostValue);
				end.put(RouteComponentFields.OWN_TIME.getName(), Long.valueOf(calendar.getTimeInMillis()));
				end.put(CommonFields.ALTITUDE.getName(), Double.valueOf(endAltitude));
				end.put(RouteComponentFields.LAST_POSITION_ID.getName(), currId);
				end.put(RouteComponentFields.LAST_POSITION_DISTANCE.getName(), endVerticalDistance);
				end.put(RouteComponentFields.LAST_POSITION_COST.getName(), verticalCost4Minute);
				end.put(RouteComponentFields.REMAINING.getName(), Integer.valueOf(remaining));
				data1.add(end);
				data.put(RouteComponentFields.NEXT_POSITION_ID.getName(), endId);
				data.put(RouteComponentFields.NEXT_POSITION_DISTANCE.getName(), endVerticalDistance);
				data.put(RouteComponentFields.NEXT_POSITION_COST.getName(), verticalCost4Minute);
				data1.add(data);

				distance = distance + endVerticalDistanceValue;
				cost = cost + verticalCostValue4Minute;
				cost = cost + lastCost;
				coordinates.add(new Point(nextLon, nextLat));
				data = new HashMap<String, Object>();
				data.put(RouteIntegrationFields.TRACK.getName(),
						GeoJsonToMapConverter.INSTANCE.convert(GeoJsonLineString.of(coordinates)));
				data.put(CommonFields.ALTITUDE.getName(), altitude);
				data.put(CommonFields.TASK_ID.getName(), taskId);
				data.put(RouteIntegrationFields.START_TIME.getName(), Long.valueOf(startTime));
			}
		}
		return cost;
	}
}
